/*
 *  Copyright (c) 2012 Brandon Ellenberger
 *
 *  This file is part of OpSatPlan.
 *
 *  OpSatPlan is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  OpSatPlan is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with OpSatPlan.  If not, see <http://www.gnu.org/licenses/>.
 */

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace OpSatPlan.LexerParser
{
    #region PddlNode

    public abstract class PddlNode
    {

        #region Members

        //private List<PddlNode> mChildren;
        //private PddlNode mParent;
        //private string mText;
	    private int mLineNumber;
	    private int mColumnNumber;
        
        #endregion

        #region Constructor

        protected PddlNode(int lineNumber, int columnNumber)
	    {
		    this.mLineNumber = lineNumber;
		    this.mColumnNumber = columnNumber;
	    }

        #endregion

        #region Properties

        public int LineNumber
        {
            get
            {
                return this.mLineNumber;
            }
        }
        public int ColumnNumber
        {
            get
            {
                return this.mColumnNumber;
            }
        }

        #endregion

    }

    #endregion

    #region DomainNode

    public class DomainNode : PddlNode
    {
        private IdentifierNode mName;
        private RequireDefinitionNode mRequirements;
        private TypedNamesListNode mTypes;
        private AtomicFormulaSkeletonsNode mPredicates;
        private ActionDefinitionsNode mActions;
        public DomainNode(IdentifierNode name, RequireDefinitionNode require, TypedNamesListNode types,
                            AtomicFormulaSkeletonsNode predicates, ActionDefinitionsNode actions,
                            int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mName = name;
            this.mRequirements = require;
            this.mPredicates = predicates;
            this.mTypes = types;
            this.mActions = actions;
        }

        public Domain BuildDomain()
        {
            Domain domain;
            List<ActionDefinition> actions;
            Dictionary<string, PredicateDefinition> predicates;
            Dictionary<string, PddlType> types;
            List<string> requirements;

            requirements = this.mRequirements.BuildRequirements();
            if (this.mTypes != null)
            {
                types = this.mTypes.BuildTypes();
                Assertion.Assert<UsageException>(types.Count != 0 && requirements.Contains("TYPING"), "Types are required when using typing");
            }
            else
                types = null;
            predicates = this.mPredicates.BuildPredicates(types);

            actions = this.mActions.BuildActions(predicates, types);

            if(types != null)
                domain = new Domain(this.mName.Identifier, actions, new List<PredicateDefinition>(predicates.Values), 
                                  requirements, new List<PddlType>(types.Values));
            else
                domain = new Domain(this.mName.Identifier, actions, new List<PredicateDefinition>(predicates.Values),
                                  requirements, new List<PddlType>());
            return domain;
        }

    }
    #endregion

    #region IdentifierNode

    public class IdentifierNode : PddlNode
    {
        private string mIdentifier;
        public IdentifierNode(string identifier, int lineNumber, int columnNumber)
            :base(lineNumber, columnNumber)
        {
            this.mIdentifier = identifier.ToUpper();
        }
        public string Identifier
        {
            get
            {
                return this.mIdentifier;
            }
        }
    }
    
    #endregion

    #region IdentifiersNode

    public class IdentifiersNode : PddlNode
    {
        private List<IdentifierNode> mIdentifiers;
        public IdentifiersNode(int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mIdentifiers = new List<IdentifierNode>();
        }
        public void Add(IdentifierNode ident)
        {
            this.mIdentifiers.Add(ident);
        }
        public void AddRange(IEnumerable<IdentifierNode> idents)
        {
            this.mIdentifiers.AddRange(idents);
        }
        public ReadOnlyCollection<IdentifierNode> Identifiers
        {
            get
            {
                return new ReadOnlyCollection<IdentifierNode>(this.mIdentifiers);
            }
        }
    }

    #endregion

    #region RequireDefinitionNode

    public class RequireDefinitionNode : IdentifiersNode
    {
        public RequireDefinitionNode(int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
        }
        public List<string> BuildRequirements()
        {
            List<string> requirements = new List<string>();
            foreach (IdentifierNode id in this.Identifiers)
            {
                requirements.Add(id.Identifier);
            }
            return requirements;
        }
    }

    #endregion

    #region TypedNamesListNode

    public class TypedNamesListNode : PddlNode
    {
        private List<TypedNamesNode> mList;
        public TypedNamesListNode(int lineNumber, int columnNumber)
            :base(lineNumber, columnNumber)
        {
            this.mList = new List<TypedNamesNode>();
        }
        public void Add(TypedNamesNode names)
        {
            this.mList.Add(names);
        }
        public void AddRange(IEnumerable<TypedNamesNode> names)
        {
            this.mList.AddRange(names);
        }
        public ReadOnlyCollection<TypedNamesNode> Names
        {
            get
            {
                return new ReadOnlyCollection<TypedNamesNode>(this.mList);
            }
        }
        public Dictionary<string, PddlType> BuildTypes()
        {
            Dictionary<string, PddlType> types = new Dictionary<string, PddlType>();
            foreach (TypedNamesNode tnn in this.mList)
            {
                tnn.BuildTypes(types);
            }
            return types;
        }
        public Dictionary<string, PddlObject> BuildObjects(Domain domain)
        {
            Dictionary<string, PddlObject> objs = new Dictionary<string, PddlObject>();
            foreach (TypedNamesNode tnn in this.mList)
            {
                tnn.BuildObjects(domain, objs);
            }
            return objs;
        }
    }
    
    #endregion

    #region TypedNamesNode

    public class TypedNamesNode : PddlNode
    {
        private IdentifiersNode mNames;
        private IdentifierNode mType;
        public TypedNamesNode(IdentifiersNode names, IdentifierNode type, int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mNames = names;
            this.mType = type;
        }
        public IdentifierNode Type
        {
            get
            {
                return this.mType;
            }
        }
        public IdentifiersNode Names
        {
            get
            {
                return this.mNames;
            }
        }
        public void BuildTypes(Dictionary<string, PddlType> types)
        {
            PddlType parentType;
            PddlType type;
            parentType = null;
            if (this.Type != null)
            {
                if (types.ContainsKey(this.Type.Identifier))
                    parentType = types[this.Type.Identifier];
                else
                {
                    parentType = new PddlType(this.Type.Identifier);
                    types.Add(parentType.Name, parentType);
                }
            }
            foreach (IdentifierNode n in this.Names.Identifiers)
            {
                if (types.ContainsKey(n.Identifier))
                {
                    Assertion.Assert<UsageException>(types[n.Identifier].Parent == parentType, "Invalid type specification.");
                }
                else
                {
                    type = new PddlType(n.Identifier, parentType);
                    types.Add(type.Name, type);
                }
            }
        }
        public void BuildObjects(Domain domain, Dictionary<string, PddlObject> objs)
        {
            PddlType type;
            type = null;
            if (this.Type != null)
            {
                type = domain.GetTypeByName(this.Type.Identifier);
            }
            foreach (IdentifierNode n in this.Names.Identifiers)
            {
                Assertion.Assert<UsageException>(!objs.ContainsKey(n.Identifier), "Object " + n.Identifier + " already exists");
                PddlObject obj = new PddlObject(n.Identifier, type);
                objs.Add(obj.Name, obj);
            }
        }
    }

    #endregion

    #region AtomicFormulaSkeletonNode

    public class AtomicFormulaSkeletonNode : PddlNode
    {
        private IdentifierNode mName;
        private TypedVariablesListNode mVariables;
        public AtomicFormulaSkeletonNode(IdentifierNode name, TypedVariablesListNode variables, int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mName = name;
            this.mVariables = variables;
        }
        public void BuildPredicates(Dictionary<string, PredicateDefinition> preds, Dictionary<string, PddlType> types)
        {
            PredicateDefinition pred;
            List<Parameter> parms;
            if (this.mVariables != null)
                parms = this.mVariables.BuildParameters(types);
            else
                parms = new List<Parameter>();
            pred = new PredicateDefinition(this.mName.Identifier, parms);
            preds.Add(pred.Name, pred);
        }
    }

    #endregion

    #region AtomicFormulaSkeletonsNode

    public class AtomicFormulaSkeletonsNode : PddlNode
    {
        private List<AtomicFormulaSkeletonNode> mAtomicFormulas;
        public AtomicFormulaSkeletonsNode(int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mAtomicFormulas = new List<AtomicFormulaSkeletonNode>();
        }
        public void Add(AtomicFormulaSkeletonNode af)
        {
            this.mAtomicFormulas.Add(af);
        }
        public void AddRange(IEnumerable<AtomicFormulaSkeletonNode> afs)
        {
            this.mAtomicFormulas.AddRange(afs);
        }
        public ReadOnlyCollection<AtomicFormulaSkeletonNode> AtomicFormulaSkeletons
        {
            get
            {
                return new ReadOnlyCollection<AtomicFormulaSkeletonNode>(this.mAtomicFormulas);
            }
        }
        public Dictionary<string, PredicateDefinition> BuildPredicates(Dictionary<string, PddlType> types)
        {
            Dictionary<string, PredicateDefinition> preds = new Dictionary<string, PredicateDefinition>();
            foreach (AtomicFormulaSkeletonNode afs in this.AtomicFormulaSkeletons)
            {
                afs.BuildPredicates(preds, types);
            }
            return preds;
        }
    }

    #endregion

    #region TypedVariablesListNode

    public class TypedVariablesListNode : PddlNode
    {
        private List<TypedVariablesNode> mVariables;
        public TypedVariablesListNode(int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mVariables = new List<TypedVariablesNode>();
        }
        public void Add(TypedVariablesNode var)
        {
            this.mVariables.Add(var);
        }
        public void AddRange(IEnumerable<TypedVariablesNode> vars)
        {
            this.mVariables.AddRange(vars);
        }
        public ReadOnlyCollection<TypedVariablesNode> Variables
        {
            get
            {
                return new ReadOnlyCollection<TypedVariablesNode>(this.mVariables);
            }
        }
        public List<Parameter> BuildParameters(Dictionary<string, PddlType> types)
        {
            List<Parameter> parms = new List<Parameter>();
            foreach(TypedVariablesNode vars in this.Variables)
            {
                vars.BuildParameters(parms, types);
            }
            return parms;
        }
    }

    #endregion

    #region TypedVariablesNode

    public class TypedVariablesNode : PddlNode
    {
        private IdentifierNode mType;
        private IdentifiersNode mTypes;
        private IdentifiersNode mVariables;
        public TypedVariablesNode(IdentifiersNode variables, IdentifierNode type, int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mVariables = variables;
            this.mType = type;
            this.mTypes = null;
        }
        public TypedVariablesNode(IdentifiersNode variables, IdentifiersNode types, int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mVariables = variables;
            this.mType = null;
            this.mTypes = types;
        }
        public TypedVariablesNode(IdentifiersNode variables, int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mVariables = variables;
            this.mType = null;
            this.mTypes = null;
        }
        public void BuildParameters(List<Parameter> parms, Dictionary<string, PddlType> types)
        {
            PddlType parentType = null;
            PddlTypeSet set = null;
            if (this.mType != null)
            {
                Assertion.Assert<UsageException>(types.ContainsKey(this.mType.Identifier), "Type " + this.mType.Identifier + " does not exist in domain");
                parentType = types[this.mType.Identifier];
            }
            if (this.mTypes != null)
            {
                set = new PddlTypeSet();
                foreach (IdentifierNode t in this.mTypes.Identifiers)
                {
                    Assertion.Assert<UsageException>(types.ContainsKey(t.Identifier), "Type " + t.Identifier + " does not exist in domain");
                    PddlType type = types[t.Identifier];
                    set.Add(type);
                }
            }
            foreach (IdentifierNode var in this.mVariables.Identifiers)
            {
                Parameter parm;
                if (parentType != null)
                {
                    parm = new Parameter(var.Identifier, parentType);
                }
                else if (set != null)
                {
                    parm = new Parameter(var.Identifier, set);
                }
                else
                {
                    parm = new Parameter(var.Identifier);
                }
                parms.Add(parm);
            }
        }
    }
    
    #endregion

    #region ActionDefinitionNode

    public class ActionDefinitionNode : PddlNode
    {
        private IdentifierNode mName;
        private TypedVariablesListNode mVariables;
        private PreconditionNode mPreconditions;
        private EffectsNode mEffects;
        public ActionDefinitionNode(IdentifierNode name, TypedVariablesListNode vars, PreconditionNode pre,
                                    EffectsNode effs, int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mName = name;
            this.mVariables = vars;
            this.mPreconditions = pre;
            this.mEffects = effs;
        }
        public ActionDefinition BuildAction(Dictionary<string, PredicateDefinition> preds, Dictionary<string, PddlType> types)
        {
            ActionDefinition action;
            List<Parameter> parms;
            List<AtomicFormulaDefinition> pre;
            List<AtomicFormulaDefinition> add;
            List<AtomicFormulaDefinition> del;
            Dictionary<string, Parameter> parmMap = new Dictionary<string, Parameter>();
            if (this.mVariables != null)
            {
                parms = this.mVariables.BuildParameters(types);
                foreach (Parameter p in parms)
                    parmMap.Add(p.Name, p);
            }
            else
            {
                parms = new List<Parameter>();
            }
            if (this.mPreconditions != null)
                pre = this.mPreconditions.BuildPreconditions(parmMap, preds);
            else
                pre = new List<AtomicFormulaDefinition>();
            if (this.mEffects != null)
            {
                add = this.mEffects.BuildAddEffects(parmMap, preds);
                del = this.mEffects.BuildDeleteEffects(parmMap, preds);
            }
            else
            {
                add = new List<AtomicFormulaDefinition>();
                del = new List<AtomicFormulaDefinition>();
            }
            action = new ActionDefinition(this.mName.Identifier, parms, pre, add, del);
            return action;
        }
    }

    #endregion

    #region ActionDefinitionsNode

    public class ActionDefinitionsNode : PddlNode
    {
        private List<ActionDefinitionNode> mActions;
        public ActionDefinitionsNode(int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mActions = new List<ActionDefinitionNode>();
        }
        public void Add(ActionDefinitionNode action)
        {
            this.mActions.Add(action);
        }
        public void AddRange(IEnumerable<ActionDefinitionNode> actions)
        {
            this.mActions.AddRange(actions);
        }
        public ReadOnlyCollection<ActionDefinitionNode> ActionDefinitions
        {
            get
            {
                return new ReadOnlyCollection<ActionDefinitionNode>(this.mActions);
            }
        }
        public List<ActionDefinition> BuildActions(Dictionary<string, PredicateDefinition> preds, Dictionary<string, PddlType> types)
        {
            List<ActionDefinition> actions = new List<ActionDefinition>();
            foreach (ActionDefinitionNode aDef in this.ActionDefinitions)
            {
                actions.Add(aDef.BuildAction(preds, types));
            }
            return actions;
        }
    }

    #endregion

    #region LiteralNode

    public class LiteralNode : AtomicFormulaNode
    {
        private bool mValue;
        public LiteralNode(IdentifierNode name, IdentifiersNode variables, bool value, int lineNumber, int columnNumber)
            : base(name, variables, lineNumber, columnNumber)
        {
            this.mValue = value;
        }
        public bool Value
        {
            get
            {
                return this.mValue;
            }
        }
    }
    
    #endregion

    #region AtomicFormulaNode

    public class AtomicFormulaNode : PddlNode
    {
        private IdentifierNode mName;
        private IdentifiersNode mVariables;
        public AtomicFormulaNode(IdentifierNode name, IdentifiersNode variables, int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mName = name;
            this.mVariables = variables;
        }
        public IdentifierNode Name
        {
            get
            {
                return this.mName;
            }
        }
        public IdentifiersNode Variables
        {
            get
            {
                return this.mVariables;
            }
        }
        public virtual AtomicFormulaDefinition BuildAtomicFormula(Dictionary<string, Parameter> parms, 
                                                                    Dictionary<string, PredicateDefinition> preds)
        {
            List<Parameter> aParms = new List<Parameter>();
            if (this.mVariables != null)
            {
                foreach (IdentifierNode id in this.mVariables.Identifiers)
                {
                    Assertion.Assert<UsageException>(parms.ContainsKey(id.Identifier), "Variable ?" + id.Identifier + " does not exist in Action Parameters");
                    aParms.Add(parms[id.Identifier]);
                }
            }
            Assertion.Assert<UsageException>(preds.ContainsKey(this.mName.Identifier), "Predicate " + this.mName.Identifier + " does not exist in domain");
            PredicateDefinition pred = preds[this.mName.Identifier];

            return new AtomicFormulaDefinition(pred, aParms);
        }
    }

    #endregion

    #region AtomicFormulasNode

    public class AtomicFormulasNode : PddlNode
    {
        private List<AtomicFormulaNode> mFormulas;
        public AtomicFormulasNode(int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mFormulas = new List<AtomicFormulaNode>();
        }
        public void Add(AtomicFormulaNode af)
        {
            this.mFormulas.Add(af);
        }
        public void AddRange(IEnumerable<AtomicFormulaNode> afs)
        {
            this.mFormulas.AddRange(afs);
        }
        public ReadOnlyCollection<AtomicFormulaNode> AtomicFormulas
        {
            get
            {
                return new ReadOnlyCollection<AtomicFormulaNode>(this.mFormulas);
            }
        }
    }

    #endregion

    #region PreconditionNode

    public class PreconditionNode : PddlNode
    {
        private AtomicFormulasNode mFormulas;
        public PreconditionNode(AtomicFormulasNode afs, int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mFormulas = afs;
        }
        public List<AtomicFormulaDefinition> BuildPreconditions(Dictionary<string, Parameter> parms, 
                                                                Dictionary<string, PredicateDefinition> preds)
        {
            List<AtomicFormulaDefinition> atomDefs = new List<AtomicFormulaDefinition>();
            if (this.mFormulas != null)
            {
                foreach (AtomicFormulaNode afn in this.mFormulas.AtomicFormulas)
                {
                    atomDefs.Add(afn.BuildAtomicFormula(parms, preds));
                }
            }
            return atomDefs;
        }
    }

    #endregion

    #region EffectsNode

    public class EffectsNode : PddlNode
    {
        private AtomicFormulasNode mFormulas;
        public EffectsNode(AtomicFormulasNode afs, int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mFormulas = afs;
        }
        public List<AtomicFormulaDefinition> BuildAddEffects(Dictionary<string, Parameter> parms,
                                                                    Dictionary<string, PredicateDefinition> preds)
        {
            List<AtomicFormulaDefinition> atomDefs = new List<AtomicFormulaDefinition>();
            if (this.mFormulas != null)
            {
                foreach (AtomicFormulaNode afn in this.mFormulas.AtomicFormulas)
                {
                    if ((afn is LiteralNode && ((LiteralNode)afn).Value == true) || afn.GetType() == typeof(AtomicFormula))
                    {
                        atomDefs.Add(afn.BuildAtomicFormula(parms, preds));
                    }
                }
            }
            return atomDefs;
        }
        public List<AtomicFormulaDefinition> BuildDeleteEffects(Dictionary<string, Parameter> parms,
                                                                    Dictionary<string, PredicateDefinition> preds)
        {
            List<AtomicFormulaDefinition> atomDefs = new List<AtomicFormulaDefinition>();
            if (this.mFormulas != null)
            {
                foreach (AtomicFormulaNode afn in this.mFormulas.AtomicFormulas)
                {
                    if (afn is LiteralNode && ((LiteralNode)afn).Value == false)
                    {
                        atomDefs.Add(afn.BuildAtomicFormula(parms, preds));
                    }
                }
            }
            return atomDefs;
        }
    }

    #endregion

    #region GroundedLiteralNode
    
    public class GroundedLiteralNode : PddlNode
    {
        private IdentifierNode mName;
        private IdentifiersNode mObjects;
        private bool mValue;
        public GroundedLiteralNode(IdentifierNode name, IdentifiersNode objs, bool value, int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mName = name;
            this.mObjects = objs;
            this.mValue = value;
        }
        public IdentifierNode Name
        {
            get
            {
                return this.mName;
            }
        }
        public IdentifiersNode Objects
        {
            get
            {
                return this.mObjects;
            }
        }
        public bool Value
        {
            get
            {
                return this.mValue;
            }
        }
    }

    #endregion 

    #region GroundedLiteralsNode

    public class GroundedLiteralsNode : PddlNode
    {
        private List<GroundedLiteralNode> mLits;
        public GroundedLiteralsNode(int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mLits = new List<GroundedLiteralNode>();
        }
        public void Add(GroundedLiteralNode lit)
        {
            this.mLits.Add(lit);
        }
        public void AddRange(IEnumerable<GroundedLiteralNode> lits)
        {
            this.mLits.AddRange(lits);
        }
        public ReadOnlyCollection<GroundedLiteralNode> GroundedLiterals
        {
            get
            {
                return new ReadOnlyCollection<GroundedLiteralNode>(this.mLits);
            }
        }
        public List<ValuedAtomicFormula> BuildLiterals(Dictionary<string, PddlObject> objects, Domain domain)
        {
            List<ValuedAtomicFormula> vafs = new List<ValuedAtomicFormula>();
            foreach(GroundedLiteralNode gl in this.GroundedLiterals)
            {
                PredicateDefinition pd = domain.GetPredicateByName(gl.Name.Identifier);
                List<PddlObject> args = new List<PddlObject>();
                if (gl.Objects != null)
                {
                    foreach (IdentifierNode o in gl.Objects.Identifiers)
                    {
                        args.Add(objects[o.Identifier]);
                    }
                }
                for (int i = 0; i < pd.Parameters.Count; i++)
                {
                    Assertion.Assert<UsageException>(pd.Parameters[i].TypeMatches(args[i]), "Types must match");
                }
                AtomicFormula af = new AtomicFormula(pd, args);
                vafs.Add(new ValuedAtomicFormula(af, gl.Value));
            }
            return vafs;
        }
    }

    #endregion

    #region StateNode

    public class StateNode : PddlNode
    {
        private GroundedLiteralsNode mLits;
        public StateNode(GroundedLiteralsNode lits, int lineNumber, int columnNumber)
            :base(lineNumber, columnNumber)
        {
            this.mLits = lits;
        }
        public List<ValuedAtomicFormula> BuildState(Dictionary<string, PddlObject> objects, Domain domain)
        {
            return this.mLits.BuildLiterals(objects, domain);
        }
    }

    #endregion

    #region ProblemNode

    public class ProblemNode : PddlNode
    {
        private IdentifierNode mProblemName;
        private IdentifierNode mDomainName;
        private TypedNamesListNode mObjects;
        private StateNode mInit;
        private StateNode mGoal;
        public ProblemNode(IdentifierNode probName, IdentifierNode domName, TypedNamesListNode objs,
                            StateNode init, StateNode goal, int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mProblemName = probName;
            this.mDomainName = domName;
            this.mObjects = objs;
            this.mInit = init;
            this.mGoal = goal;
        }
        public Problem BuildProblem(Domain domain)
        {
            Problem problem = null;
            Assertion.Assert<UsageException>(domain.Name == this.mDomainName.Identifier, "The domain name of the problem must match the domain " + domain.Name);
            Dictionary<string, PddlObject> objects = this.mObjects.BuildObjects(domain);
            List<ValuedAtomicFormula> init = this.mInit.BuildState(objects, domain);
            List<ValuedAtomicFormula> goal = this.mGoal.BuildState(objects, domain);

            problem = new Problem(domain, this.mProblemName.Identifier, init, goal, new List<PddlObject>(objects.Values));
            return problem;
        }
    }

    #endregion

    public class IdentifierListNode : PddlNode
    {
        private AtomicFormulaNode mMainFormula;
        private AtomicFormulasNode mFormulas;
        public IdentifierListNode(AtomicFormulaNode main, 
                                    AtomicFormulasNode formulas, int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mMainFormula = main;
            this.mFormulas = formulas;
        }
        public void BuildIdentifiers(ActionDefinition action, Dictionary<string, Parameter> parms, Dictionary<string, PredicateDefinition> preds)
        {
            IdentifierDefinition idDef;
            idDef = null;

            idDef = this.BuildIdentifier(this.mMainFormula, action, parms, preds);
            if (idDef != null && this.mFormulas != null)
            {
                foreach (AtomicFormulaNode afn in this.mFormulas.AtomicFormulas)
                {
                    IdentifierDefinition sub = null;
                    sub = this.BuildIdentifier(afn, action, parms, preds);
                    if (sub != null)
                        idDef.Add(sub);
                }
            }
            action.AddIdentifier(idDef);
        }
        private IdentifierDefinition BuildIdentifier(AtomicFormulaNode afn, ActionDefinition action,
                                                    Dictionary<string, Parameter> parms,
                                                    Dictionary<string, PredicateDefinition> preds)
        {
            IdentifierDefinition idDef = null;
            if ((afn is LiteralNode && ((LiteralNode)afn).Value == true) || afn.GetType() == typeof(AtomicFormula))
            {
                AtomicFormulaDefinition af = afn.BuildAtomicFormula(parms, preds);
                if (action.Preconditions.Any(f => f == af))
                {
                    idDef = new IdentifierDefinition(af, true, IdentifierTypes.Precondition);
                }
                else if (action.AddEffects.Any(f => f == af))
                {
                    idDef = new IdentifierDefinition(af, true, IdentifierTypes.Effect);
                }
            }
            else if ((afn is LiteralNode && ((LiteralNode)afn).Value == false))
            {
                AtomicFormulaDefinition af = afn.BuildAtomicFormula(parms, preds);
                idDef = new IdentifierDefinition(af, false, IdentifierTypes.Effect);
            }
            return idDef;
        }
    }

    public class ActionIdentifierNode : PddlNode
    {
        private IdentifierNode mActionName;
        private IdentifierListsNode mLists;
        public ActionIdentifierNode(IdentifierNode name, IdentifierListsNode ids, int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mActionName = name;
            this.mLists = ids;
        }
        public void BuildIdentifiers(Domain domain)
        {
            var action = (from act in domain.ActionDefinitions where act.Name == this.mActionName.Identifier select act).First();
            Dictionary<string, Parameter> parms = new Dictionary<string, Parameter>();
            Dictionary<string, PredicateDefinition> preds = new Dictionary<string, PredicateDefinition>();

            foreach (Parameter parm in action.Parameters)
                parms.Add(parm.Name, parm);
            foreach (PredicateDefinition pred in domain.PredicateDefinition)
                preds.Add(pred.Name, pred);

            this.mLists.BuildIdentifiers(action, parms, preds);
        }
      
    }

    public class ActionIdentifiersNode : PddlNode
    {
        private List<ActionIdentifierNode> mActionIds;
        public ActionIdentifiersNode(int lineNumber, int columnNumber)
            :base(lineNumber, columnNumber)
        {
            this.mActionIds = new List<ActionIdentifierNode>();
        }
        public void Add(ActionIdentifierNode ain)
        {
            this.mActionIds.Add(ain);
        }
        public void AddRange(IEnumerable<ActionIdentifierNode> ains)
        {
            this.mActionIds.AddRange(ains);
        }
        public ReadOnlyCollection<ActionIdentifierNode> ActionIdentifiers
        {
            get
            {
                return new ReadOnlyCollection<ActionIdentifierNode>(this.mActionIds);
            }
        }
        public void BuildIdentifiers(Domain domain)
        {
            foreach (ActionIdentifierNode ain in this.mActionIds)
            {
                ain.BuildIdentifiers(domain);
            }
        }
    }

    public class IdentifierListsNode : PddlNode
    {
        private List<IdentifierListNode> mIdLists;
 
        public IdentifierListsNode(int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mIdLists = new List<IdentifierListNode>();
        }
        public void Add(IdentifierListNode iln)
        {
            this.mIdLists.Add(iln);
        }
        public void AddRange(IEnumerable<IdentifierListNode> ilns)
        {
            this.mIdLists.AddRange(ilns);
        }
        public ReadOnlyCollection<IdentifierListNode> IdentifierLists
        {
            get
            {
                return new ReadOnlyCollection<IdentifierListNode>(this.mIdLists);
            }
        }
        public void BuildIdentifiers(ActionDefinition action, Dictionary<string, Parameter> parms, Dictionary<string, PredicateDefinition> preds)
        {
            foreach (IdentifierListNode iln in this.mIdLists)
            {
                iln.BuildIdentifiers(action, parms, preds);
            }
        }
    }

    public class IdentifierDefinitionNode : PddlNode
    {
        private IdentifierNode mName;
        private IdentifierNode mDomainNode;
        private ActionIdentifiersNode mActionIds;
        public IdentifierDefinitionNode(IdentifierNode name, IdentifierNode domName, ActionIdentifiersNode actionIds,
                                        int lineNumber, int columnNumber)
            : base(lineNumber, columnNumber)
        {
            this.mName = name;
            this.mDomainNode = domName;
            this.mActionIds = actionIds;
        }

        public void BuildIdentifiers(Domain domain)
        {
            this.mActionIds.BuildIdentifiers(domain);
        }
    }

}